package control;

import java.io.Serializable;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import entity.Event;
import entity.IndoorEvent;
import entity.OutdoorEvent;
import entity.User;
import enumeration.NotificationType;
import exception.InvalidDateException;
import exception.NotCreatorException;

@Stateless
public class EventManager implements Serializable {

	private static final long serialVersionUID = 1L;

	@PersistenceContext(unitName = "meteoCalPU")
	EntityManager em;

	@EJB
	UserManager um;
	@EJB
	WeatherManager wm;
	
	@EJB
	NotificationManager nm;

	public void create(Event entity) throws InvalidDateException {
		// if the start date is greater than the end date, throws an exception
		if (entity.getStartDate().getTime() > entity.getEndDate().getTime())
			throw new InvalidDateException("invalid date");
		// sets the organizer user
		entity.setOrganizerUser(um.getLoggedUser());
		// checks the event type and store the specific event
		if (entity instanceof IndoorEvent) {
			IndoorEvent ie = (IndoorEvent) entity;
			em.persist(ie);
		} else if (entity instanceof OutdoorEvent) {
			OutdoorEvent oe = (OutdoorEvent) entity;
			// updates the weather
			wm.updateWeatherData(oe);
			em.persist(oe);
		}
	}

	/**
	 * remove the specified event
	 * 
	 * @param e
	 *            event
	 * @throws Exception
	 *             if the user is not authorized
	 */
	public void remove(Event e) throws NotCreatorException {
		User organizerUser = ((TypedQuery<User>) em.createQuery(
				"SELECT e.organizerUser FROM Event e WHERE e=:event")
				.setParameter("event", e)).getSingleResult();
		// if he's the organizer
		if (organizerUser.getId() == um.getLoggedUser().getId()) {

			em.createQuery("DELETE FROM Notification n WHERE n.event=:e")
					.setParameter("e", e).executeUpdate();
			if (e instanceof OutdoorEvent)
				em.createQuery(
						"DELETE FROM WeatherInformation w WHERE w.event=:e")
						.setParameter("e", e).executeUpdate();

			em.createQuery("DELETE FROM Event e WHERE e.id=:id")
					.setParameter("id", e.getId()).executeUpdate();
		}
		// otherwise shows an error
		else {
			throw new NotCreatorException("you're not the creator!");
		}
	}

	/**
	 * update the given event storing it in the database
	 * 
	 * @param e
	 *            the event
	 * @throws InvalidDateException
	 * @throws Exception
	 *             if the user is not authorized
	 */
	public void updateEvent(Event e,boolean notifiesUsers) throws NotCreatorException,
			InvalidDateException {
		if (e.getOrganizerUser().getId() == um.getLoggedUser().getId()) {
			// if the start date is greater than the end date, throws an
			// exception
			if (e.getStartDate().getTime() > e.getEndDate().getTime()) {
				throw new InvalidDateException("invalid date");
			}
			if (e instanceof OutdoorEvent) {
				wm.deleteWeatherInformationByEvent((OutdoorEvent) e);
				wm.updateWeatherData((OutdoorEvent) e);
			}
			// updates the event
			em.merge(e);
			//notifies the users about the update
			if(notifiesUsers) {
			for(User u:e.getInvitedUsers())
				nm.createNotification(u, e, NotificationType.UPDATE_EVENT,"event updated");
			}
		} else
			throw new NotCreatorException("your're not the creator!");
	}

	/**
	 * find the event by id
	 * 
	 * @param id
	 *            id of the event
	 * @return
	 */
	public Event find(int id) {
		return em.find(Event.class, id);
	}

	/**
	 * loads the events created by the current user
	 * 
	 * @return the list of events created
	 */
	public List<Event> loadCreatedEvents() {
		return this.loadEventsByUser(um.getLoggedUser());
	}

	public List<OutdoorEvent> loadOutdoorEvents(User u) {
		TypedQuery<OutdoorEvent> tq = (TypedQuery<OutdoorEvent>) em
				.createQuery(
						"SELECT e FROM OutdoorEvent e WHERE e.organizerUser=:user")
				.setParameter("user", u);
		return tq.getResultList();
	}

	/**
	 * loads the events by the user
	 * 
	 * @param user
	 *            user
	 * @return the list of events
	 */
	public List<Event> loadEventsByUser(User user) {
		TypedQuery<Event> tq = (TypedQuery<Event>) em.createQuery(
				"SELECT e FROM Event e WHERE  e.organizerUser=:user")
				.setParameter("user", user);
		System.out.println(tq);
		return tq.getResultList();
	}

}